- Senior Software Engineer
- CDI co-spec lead
- Red Hat, Inc.
- @antoine_sd
- www.next-presso.com
- github.com/antoinesd
| One of the most powerful feature of the CDI specification |
| Not really popularized maybe given the high level of abstraction |
| Observer pattern to listen for container initialization lifecycle events |
| Comprehensive access to and modification of the container metadata model |
Service provider of the service javax.enterprise.inject.spi.Extension declared in META-INF/services |
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.Extension;
class CdiExtension implements Extension {
void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd) {
}
...
void afterDeploymentValidation(@Observes AfterDeploymentValidation adv) {
}
}Injection points, parameterized types, programmatic bean
Annotated types, events, injection Targets, transactional Observers
| Open-source integration framework based on known Enterprise Integration Patterns |
| Bean binding and integration with Spring, Blueprint, Guice and CDI |
@EndpointInject(uri="jms:queue:foo")
Endpoint endpoint;
@PropertyInject(value = "timeout", defaultValue = "5000")
int timeout;
@BeanInject("foo")
FooBean foo;
@Produce(uri = "mock:foo")
ProducerTemplate producer;
@Consume(uri="jms:queue:foo")
void onFoo(@Body String body) {
}| Bring support for both Camel and CDI beans… |
ProcessAnnotatedType EventAnnotatedType<X>public interface AnnotatedType<X> extends Annotated {
public Class<X> getJavaClass();
public Set<AnnotatedConstructor<X>> getConstructors();
public Set<AnnotatedMethod<? super X>> getMethods();
public Set<AnnotatedField<? super X>> getFields();
}ProcessAnnotatedType<X>public interface ProcessAnnotatedType<X> {
public AnnotatedType<X> getAnnotatedType();
public void setAnnotatedType(AnnotatedType<X> type);
public void veto();
}ProcessInjectionTarget EventInjectionTarget<T>public interface InjectionTarget<T> extends Producer<T> {
public void inject(T instance, CreationalContext<T> ctx);
public void postConstruct(T instance);
public void preDestroy(T instance);
}ProcessInjectionTarget<T>public interface ProcessInjectionTarget<X> {
public AnnotatedType<X> getAnnotatedType();
public InjectionTarget<X> getInjectionTarget();
public void setInjectionTarget(InjectionTarget<X> injectionTarget);
public void addDefinitionError(Throwable t);
}class CdiCamelExtension implements Extension {
Set<AnnotatedType<?>> camelBeans = new HashSet<>());
void camelAnnotations(@Observes @WithAnnotations({BeanInject.class, (1)
Consume.class, EndpointInject.class, Produce.class, PropertyInject.class})
ProcessAnnotatedType<?> pat) {
camelBeans.add(pat.getAnnotatedType());
}
<T> void camelBeansPostProcessor(@Observes ProcessInjectionTarget<T> pit) {
if (camelBeans.contains(pit.getAnnotatedType())) (2)
pit.setInjectionTarget(new CamelInjectionTarget<>(pit.getInjectionTarget()));
}
}| 1 | Detect all the types containing Camel annotations with @WithAnnotations |
| 2 | Decorate the InjectionTarget corresponding to these types with a custom post-processor |
InjectionTarget Decorationclass CamelInjectionTarget<T> implements InjectionTarget<T> {
InjectionTarget<T> delegate;
DefaultCamelBeanPostProcessor processor;
CamelInjectionTarget(InjectionTarget<T> target) {
delegate = target;
processor = new DefaultCamelBeanPostProcessor();
}
@Override
public void inject(T instance, CreationalContext<T> ctx) {
delegate.inject(instance, ctx);
processor.postProcessBeforeInitialization(instance); (1)
}
}| 1 | Call the Camel default bean post-processor after CDI injection |
from("jms:queue:{{input}}?transactionManager=#jtaTM")
.id("Input Consumer")
.onException().log("Rolling back message with ID ${header.JMSMessageID}")
.rollback().id("Rollback Transaction")
.end()
.log("Receiving message with ID ${header.JMSMessageID}: ${body}")
.choice()
.when(header("JMSRedelivered").isEqualTo(Boolean.TRUE))
.to("jms:queue:{{error}}?transactionManager=#jtaTM").id("Error Producer")
.otherwise()
.beanRef("transformer").id("Transformer")
.to("murex:trade-repository").id("Trade Repository")
.choice()
.when(not(isInserted))
.log("Error received: ${body}").id("Trade Repository Error")
.throwException(new CamelExecutionException("Import Failed")))
.otherwise()
.log("Answer received: ${body}").id("Trade Repository Answer");| Camel DSL Aspect Oriented Programming with CDI observer methods as pointcut and advice definitions |
void interceptProcessor(@Observes @Before @Node("foo") Exchange exchange) {
// intercept the exchange before processor with id "foo"
}void interceptProcessorBody(@Observes @Node("foo") @Body String body) {
// use Camel parameter binding annotations for the joint point context
}void receive(@Observes(during=AFTER_SUCCESS) @Endpoint("bar") Exchange exchange) {
// exchange sent to endpoint "bar" when the transaction is committed successfully
}ProcessObserverMethod EventObserverMethod<T>public interface ObserverMethod<T> {
public Class<?> getBeanClass();
public Type getObservedType();
public Set<Annotation> getObservedQualifiers();
public Reception getReception();
public TransactionPhase getTransactionPhase();
public void notify(T event);
}ProcessObserverMethod<T, X>public interface ProcessObserverMethod<T, X> {
public AnnotatedMethod<X> getAnnotatedMethod();
public ObserverMethod<T> getObserverMethod();
public void addDefinitionError(Throwable t);
}Annotated types, alternatives, interceptors, producers
Open-source Java library providing monitoring primitives like Counter, Gauge, Histogram, Meter, timer, … |
Provides a MetricRegistry that articulates modules and reporters |
| Defines annotations for AOP frameworks like Spring AOP, AspectJ, Guice (AOP Alliance) and CDI, e.g.: |
class TimedMethodBean {
@Timed
void timedMethod() {
// Timer name => TimedMethodBean.timedMethod
}
}Bean InterfaceIntegrate the MetricRegistry as a CDI Bean |
public interface Bean<T> extends Contextual<T>, BeanAttributes<T> {
public Class<?> getBeanClass();
public Set<InjectionPoint> getInjectionPoints();
// Contextual<T>
public T create(CreationalContext<T> creationalContext);
public void destroy(T instance, CreationalContext<T> creationalContext);
// BeanAttributes<T>
public Set<Type> getTypes();
public Set<Annotation> getQualifiers();
public Class<? extends Annotation> getScope();
public String getName();
public Set<Class<? extends Annotation>> getStereotypes();
public boolean isAlternative();
}MetricRegistry Beanclass MetricRegistryBean implements Bean<MetricRegistry> {
public Set<Annotation> getQualifiers() {
return Collections.unmodifiableSet(new HashSet<>(
Arrays.asList(DefaultLiteral.INSTANCE, AnyLiteral.INSTANCE)));
}
public MetricRegistry create(CreationalContext<MetricRegistry> context) {
return new MetricRegistry();
}
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}
...
}AfterBeanDiscovery Eventpublic interface AfterBeanDiscovery {
public void addDefinitionError(Throwable t);
public void addBean(Bean<?> bean);
public void addObserverMethod(ObserverMethod<?> observerMethod);
public void addContext(Context context);
public <T> AnnotatedType<T> getAnnotatedType(Class<T> type, String id);
public <T> Iterable<AnnotatedType<T>> getAnnotatedTypes(Class<T> type);
}class CdiMetricsExtension implements Extension {
void defaultMetricRegistry(@Observes AfterBeanDiscovery abd, BeanManager manager) {
if (manager.getBeans(MetricRegistry.class, AnyLiteral.INSTANCE).isEmpty()) (1)
abd.addBean(new MetricRegistryBean()); (2)
}
}| 1 | Check if there is a bean of type MetricRegisty enabled |
| 2 | If any add a default MetricRegisty bean implementation |
Else, the end-user deployed MetricRegistry bean is used, e.g.: |
@Produces
@ApplicationScoped
MetricRegistry customMetricRegistry() {
}| Use interceptors for Metrics annotation AOP |
class MetricsExtension implements Extension {
<X> void metricsAnnotations(@Observes @WithAnnotations(Timed.class)
ProcessAnnotatedType<X> pat) {
Set<AnnotatedMethod<? super X>> decoratedMethods = new HashSet<>();
for (AnnotatedMethod<? super X> method : pat.getAnnotatedType().getMethods()) {
if (method.isAnnotationPresent(Timed.class)) {
decoratedMethods.add(
new AnnotatedMethodDecorator<>(method, new TimedBindingLiteral()));
}
}
pat.setAnnotatedType(
new AnnotatedTypeDecorator<>(pat.getAnnotatedType(), decoratedMethods));
}
}@Interceptor
@TimedBinding
@Priority(Interceptor.Priority.LIBRARY_BEFORE)
class TimedInterceptor {
@Inject MetricRegistry registry;
@AroundInvoke
Object timedMethod(InvocationContext context) throws Exception {
String name = context.getMethod().getAnnotation(Timed.class).name();
Timer timer = registry.timer(name);
Timer.Context time = timer.time();
try {
return context.proceed();
} finally {
time.stop();
}
}
}@Inject
private Meter hits; (1)
@Timed(name = "calls") (2)
public void cachedMethod() {
if (hit) hits.mark();
}
@Produces @Metric(name = "cache-hits") (3)
private Gauge<Double> cacheHitRatioGauge(Meter hits, Timer calls) {
return () -> calls.getOneMinuteRate() == 0 ? Double.NaN :
hits.getOneMinuteRate() / calls.getOneMinuteRate();
}| 1 | Metric injection from the registry |
| 2 | Method instrumentation with CDI interceptors |
| 3 | Produce a custom Metric instance by composing others |
| Slides generated with Asciidoctor and DZSlides backend |
| Original slide template - Dan Allen & Sarah White |
| Camel CDI Extension - github.com/astefanutti/camel-cdi |
| Metrics CDI Extension - github.com/astefanutti/metrics-cdi |